#pragma once
#include "index.hpp"
#include "util.hpp"
#include <jsoncpp/json/json.h>
#include <algorithm>

// 独立命名空间
namespace ns_searcher
{
    // 保证多个词指向同一文章时,进行去重
    struct InvertedElem_Set
    {
        InvertedElem_Set(){}
        uint64_t doc_id=0;      // 文档id
        // 不同的word对应同一个id,这里用来去重,只显示一篇文章
        std::vector<std::string> key_words;  
        int weight=0;           // 权重
    };
    // 搜索类
    class Searcher
    {
    private:
        ns_index::Index *index;
    public:
        Searcher(){}
        ~Searcher(){}
        // 传入去标签之后的整合文档 "./data/raw_html/raw.txt"
        void InitSearcher(const std::string& file_path) 
        {
            // 1、获取和建立index对象
            index=ns_index::Index::GetInstance();
            std::cout<<"获取index单例成功……"<<std::endl;
            // 2、用index对象构建索引
            index->BuildIndex(file_path);
            std::cout<<"建立正排和倒排索引成功……"<<std::endl;
        }
        // query为用户的搜索语句,json_string 返回给用户浏览器的搜索结果
        // 包含去重的功能
        void Search_Set(const std::string& query,std::string* json_string)
        {
            // 1、对用户输入的搜索语句分词
            std::vector<std::string> words;
            ns_util::JiebaUtil::CutString(query,&words);
            // 2、对分出来的各种词,进行index查到
            // tokens_map不同word映射到同一篇文章时,把它们整合到InvertedElem_Set中
            std::unordered_map<uint64_t,InvertedElem_Set> tokens_map;
            for(std::string& word:words)
            {
                boost::to_lower(word);
                ns_index::InvertedList* inverted_list=index->GetInvertedIndex(word);
                if(inverted_list==nullptr) continue;
                
                for(auto& elem: *inverted_list)
                {
                    InvertedElem_Set& ines=tokens_map[elem.doc_id];
                    ines.doc_id=elem.doc_id;
                    ines.key_words.push_back(elem.key_word);
                    // 每次词都有对应这篇文章的权重,整合在一起便是他们整体的权重
                    ines.weight+=elem.weight;
                }
            }
            std::vector<InvertedElem_Set> inverted_elem_all; // 获取query所有倒排拉链中的元素,同时去重
            for(auto& iter:tokens_map)
            {
                inverted_elem_all.push_back(iter.second);
            }
            // 3、汇总查找结果,按相关性(weight)进行降序排列
            std::sort(inverted_elem_all.begin(),inverted_elem_all.end(),
            [](const InvertedElem_Set& e1,const InvertedElem_Set& e2){return e1.weight>e2.weight;});
            // 4、根据查找出来的结果构建json串,返回给用户 -- jsoncpp
            Json::Value root;
            for(auto& elem:inverted_elem_all)
            {
                ns_index::DocInfo* doc=index->GetForwardIndex(elem.doc_id);
                if(doc==nullptr) continue;
                Json::Value item;
                item["title"]=doc->title;
                // 只获取内容里面的一部分描述,同时通过关键字进行摘要
                item["describe"]=GetDesc(doc->content,elem.key_words[0]); // 只取内容里面的一部分描述,即摘要
                item["url"]=doc->url;
                item["id"]=(int)doc->doc_id;
                item["weight"]=elem.weight;
                root.append(item);
            }
            Json::FastWriter w;
            *json_string=w.write(root);
        }
        // 自定义获取摘要
        std::string GetDesc(const std::string& content,std::string& key_word)
        {
            const int  prev_stop=50;
            const int  next_stop=100;
            // 不能通过find查找,通过关键字构建索引时,都把它们小写化了,原文并没有小写化
            // std::size_t pos=content.find(key_word); 
            auto iter=std::search(content.begin(),content.end(),key_word.begin(),key_word.end(),[](int x,int y)
            {
                return std::tolower(x)==std::tolower(y);
            });
            if(iter==content.end()) return "None1";
            
            int pos=std::distance(content.begin(),iter);
            int start=0,end=content.size()-1;
            if(pos-prev_stop>start) start=pos-prev_stop;
            if(pos+next_stop<end) end=pos+next_stop;
            if(start>=end) return "None2";
            return content.substr(start,end-start)+"………";
        }
    };
}
